﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using TomanuExtensions;

namespace RaytracerLib
{
    [DebuggerDisplay("{GetType().Name}; Count={Count}")]
    public class ObjectsList<T> : IList<T>, ICollection<T>, IEnumerable<T>
        where T : SceneElement
    {
        private Scene m_scene;
        private List<T> m_list = new List<T>();

        public Scene Scene
        {
            get
            {
                return m_scene;
            }
            set
            {
                m_scene = value;

                foreach (var obj in m_list)
                    obj.Scene = m_scene;
            }
        }

        public T this[string a_name]
        {
            get
            {
                return this.FirstOrDefault(el => el.Name == a_name);
            }
            set
            {
                value.Name = a_name;
                T old = m_list.FirstOrDefault(el => el.Name == a_name);

                if (old != null)
                    this[IndexOf(old)] = value;
                else
                    Add(value);
            }
        }

        public void Insert(int a_index, T a_item)
        {
            if (String.IsNullOrWhiteSpace(a_item.Name))
                throw new ArgumentException();

            a_item.Name = StringExtensions.FindUniqueName(a_item.Name,
                from obj in m_list select obj.Name);

            if (m_list.Contains(a_item))
                throw new InvalidOperationException();

            if (m_list.Any(el => el.Name == a_item.Name))
                throw new InvalidOperationException();

            m_list.Insert(a_index, a_item);
            a_item.Scene = m_scene;
        }

        public T this[int a_index]
        {
            get
            {
                return m_list[a_index];
            }
            set
            {
                RemoveAt(a_index);
                Insert(a_index, value);
            }
        }

        public void Add(T a_item)
        {
            Insert(Count, a_item);
        }

        public void Clear()
        {
            while (Count != 0)
                RemoveAt(0);
        }

        public int IndexOf(T a_item)
        {
            return m_list.IndexOf(a_item);
        }

        public bool Contains(T a_item)
        {
            return m_list.Contains(a_item);
        }

        public void CopyTo(T[] a_array, int a_array_index)
        {
            m_list.CopyTo(a_array, a_array_index);
        }

        public int Count
        {
            get
            {
                return m_list.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public void RemoveAt(int a_index)
        {
            m_list.Remove(this[a_index]);
        }

        public virtual bool Remove(T a_item)
        {
            if (m_list.Remove(a_item))
            {
                a_item.Scene = null;
                return true;
            }
            else
                return false;
        }

        public bool Remove(string a_name)
        {
            return Remove(this[a_name]);
        }

        public IEnumerator<T> GetEnumerator()
        {
            return m_list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return m_list.GetEnumerator();
        }
    }
}